package cn.shadow.game.chess.CHnchess.board;


import cn.shadow.game.chess.CHnchess.chessman.*;
import cn.shadow.game.chess.CHnchess.type.Camp;
import cn.shadow.game.chess.CHnchess.type.ChessType;
import cn.shadow.proto.client.CHnChessClientProtoContent.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 整个棋盘类
 */
public class CHnChessBoard implements Board {

    Logger logger = LoggerFactory.getLogger(CHnChessBoard.class);

    private Map<Integer, Position> table = new HashMap<>();

    private boolean isOver;

    private Stack<Mover> movers = new Stack<>();
    /**
     * 选中
     */
    private Chess chose;

    private boolean isBlack;

    @Override
    public Chess getChose() {
        return chose;
    }

    @Override
    public boolean isBlack() {
        return isBlack;
    }

    @Override
    public void reset() {
        movers.clear();
        init();
        isOver = false;
    }

    public Board copy(Board board) {
        CHnChessBoard copy = new CHnChessBoard();
        Map<Integer, Position> copyTable = new HashMap<>();
        for (Map.Entry<Integer, Position> entry : this.getTable().entrySet()) {
            int key = entry.getKey();
            Position value = entry.getValue();
            Chess chess = value.getChess();
            if (chess != null) {
                copyTable.put(key, value.copy(value.getChess().copy(chess.getType(), copy, chess.getCamp()), copy));
            } else {
                copyTable.put(key, value.copy(null, copy));
            }
        }
        copy.setTable(copyTable);
        return copy;
    }

    public void setTable(Map<Integer, Position> table) {
        this.table = table;
    }


    public void click(int row, int col) {
        if (isOver) {
            logger.info(String.format("click,x=%s,y=%s", col, row));
            return;
        }
        Position point = getPoint(row, col);
        Chess chess = point.getChess();
        if (chess != null) {
            if (chess.isBlack() != isBlack && chose == null) {
                return;
            }
            if (chose == null) {
                chess.setChosen(true);
                chose = chess;
                logger.info(String.format("选中,棋子=%s", chose));
            } else {
                if (chess.getCamp() == chose.getCamp()) {
                    chose.setChosen(false);
                    chess.setChosen(true);
                    chose = chess;
                } else {
                    if (moveTo(chose, point)) {
                        chose.setChosen(false);
                        chose = null;
                        isBlack = !isBlack;
                    } else {
                        chose.setChosen(false);
                    }
                }
            }
        } else {
            if (chose != null) {
                if (moveTo(chose, point)) {
                    chose.setChosen(false);
                    isBlack = !isBlack;
                } else {
                    chose.setChosen(false);
                }
                chose = null;
            }
        }
    }


    private boolean canGo(Chess mover, Position targetPoint) {
        //创建一个副本
        //走一下当前的点,走完后判断是否被将军了,如果被将,则返回false;
        int pointRow = targetPoint.getRow();
        int pointCol = targetPoint.getCol();
        Board copy = mover.getBoard().copy(mover.getBoard());
        int row = mover.getRow();
        int col = mover.getCol();
        Position self = copy.getPoint(row, col);
        Chess selfChess = self.getChess();
        Position target = copy.getPoint(pointRow, pointCol);
        copy.doMoveTo(selfChess, target);
        Map<Camp, List<Chess>> campListMap = copy.checkMate();
        Camp camp = mover.getCamp();
        List<Chess> chessList = campListMap.get(camp);
        return chessList.isEmpty();
    }

    /**
     * 移动
     *
     * @param mover  棋子
     * @param target 位置
     * @return 是否移动成功
     */
    public boolean moveTo(Chess mover, Position target) {
        List<Position> canAttackPoint = mover.getCanAttackPosition();
        List<Position> canSetPoint = mover.getCanSetPosition();
        Chess targetChess = target.getChess();
        if (targetChess != null) {
            //如果有棋子,使用可攻击列表
            if (!canAttackPoint.contains(target)) {
                return false;
            }
        } else {
            //无棋子,使用放置列表
            if (!canSetPoint.contains(target)) {
                return false;
            }
        }
        //判断是否会导致己方被将军
        boolean canGo = canGo(mover, target);
        if (!canGo) {
            return false;
        }
        doMoveTo(mover, target);
        Camp camp = mover.getCamp();
        if (AfterMove(camp)) { //如果一方已经无棋可走,游戏结算
            setOver(true);
            logger.info("game over");
            return false;
        }
        return true;
    }

    /**
     * 结束判定,走完之后,判断双方是否无棋可走
     * 如果没有棋子就使用放置列表
     *
     * @param camp
     */
    private boolean AfterMove(Camp camp) {
        Set<Chess> chessList = new HashSet<>();
        for (Position point : getTable().values()) {
            Chess chess = point.getChess();
            if (chess != null && chess.getCamp() != camp) {
                List<Position> canSetPoint = chess.getCanSetPosition();
                for (Position position : canSetPoint) {
                    if (canGo(chess, position)) {
                        chessList.add(chess);
                    }
                }
            }
        }
        return chessList.isEmpty();
    }


    public void doMoveTo(Chess mover, Position target) {
        ChessMover chessMover = new ChessMover(mover.getPosition(), target);
        movers.push(chessMover);
        logger.info(String.format("有棋子移动,src=%s,target=%s", chessMover.srcPosition(), chessMover.targetPosition()));
        if (target.getChess() != null) {
            if (target.getChess().getType() == ChessType.SHUAI) { //防止观战出现空指针异常
                return;
            }
        }
        Position temp = mover.getPosition(); //移动后位置切换
        temp.setChess(null);
        target.setChess(mover);
        mover.setPosition(target);
    }

    public Stack<Mover> getMovers() {
        return movers;
    }

    @Override
    public void regret() {
        Stack<Mover> movers = getMovers();
        if (movers.isEmpty()) {
            return;
        }
        Mover step = movers.pop();
        Position mover = step.srcPosition(); //移动方
        Position target = step.targetPosition();//移动点
        Position movePoint = getPoint(mover.getRow(), mover.getCol());
        Position targetPoint = getPoint(target.getRow(), target.getCol());
        Chess chess = targetPoint.getChess();
        movePoint.setChess(chess);
        chess.setPosition(movePoint);
        int camp = chess.getCamp().getValue();
        int type = chess.getType().value();
        if (camp == 0 && type == 0) { //这个点是空的
            targetPoint.setChess(null);
        } else {
            Chess temp = ChessFactory.createChess(this, Camp.getCampByValue(camp), Objects.requireNonNull(ChessType.getTypeByValue(type)));
            targetPoint.setChess(temp);
            assert temp != null;
            temp.setPosition(targetPoint);
        }
    }

    public void setOver(boolean over) {
        isOver = over;
    }

    /**
     * @param chess 观战时选中的点
     * @return 获得可以攻击的点
     */
    @Override
    public List<Position> getCanAttackPosition(Chess chess) {
        List<Position> chessSet = new ArrayList<>();
        List<Position> canAttackPoint = chess.getCanAttackPosition();
        for (Position position : canAttackPoint) {
            Chess c = position.getChess();
            if (canGo(chess, position) && c != null) {
                chessSet.add(position);
            }
        }
        return chessSet;
    }

    /**
     * public byte canAttackPositionMsg(Chess chess) {
     * <p>
     * }
     * <p>
     * //    public String CanAttackPointToString(int row, int col) {
     * //        Position point = getPoint(row, col);
     * //        if (point == null || point.getChess() == null) {
     * //            return null;
     * //        }
     * //        List<Position> chessList = getCanAttackPoint(point.getChess());
     * //        StringJoiner joiner = new StringJoiner(";");
     * //        joiner.add(point.toXiangPoint().toString());
     * //        chessList.forEach(chess -> joiner.add(point.toXiangPoint().toString()));
     * //        return joiner.toString();
     * //    }
     * <p>
     * <p>
     * public CHnChessBoard() {
     * table = new HashMap<>(ROW * COL);
     * init();
     * }
     * <p>
     * /**
     * 整个棋盘的初始化
     * 特殊的点:
     * (7,3),(7,4)(7,5)
     * (8,3),(8,4)(8,5)
     * (9,3),(9,4)(9,5)
     */
    public void init() {
        table.clear();
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < COL; j++) {
                table.put(100 * i + j, new ChessPosition(i, j, this));
            }
        }
        initBlack();
        initRed();
    }

    public void show() {
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < COL; j++) {
                Position point = getPoint(i, j);
                System.out.print(point.show());
            }
            System.out.println();
        }

    }

    @Override
    public Map<Integer, Position> getTable() {
        return table;
    }


    private void initBlack() {
        initPoint(9, 4, new Commander(this, Camp.BLACK));
        initPoint(9, 3, new Shi(this, Camp.BLACK));
        initPoint(9, 5, new Shi(this, Camp.BLACK));
        initPoint(9, 2, new Xiang(this, Camp.BLACK));
        initPoint(9, 6, new Xiang(this, Camp.BLACK));
        initPoint(9, 1, new Ma(this, Camp.BLACK));
        initPoint(9, 7, new Ma(this, Camp.BLACK));
        initPoint(9, 0, new Che(this, Camp.BLACK));
        initPoint(9, 8, new Che(this, Camp.BLACK));
        initPoint(7, 1, new Pao(this, Camp.BLACK));
        initPoint(7, 7, new Pao(this, Camp.BLACK));
        initPoint(6, 0, new Bing(this, Camp.BLACK));
        initPoint(6, 2, new Bing(this, Camp.BLACK));
        initPoint(6, 4, new Bing(this, Camp.BLACK));
        initPoint(6, 6, new Bing(this, Camp.BLACK));
        initPoint(6, 8, new Bing(this, Camp.BLACK));

    }

    private void initRed() {

        initPoint(0, 4, new Commander(this, Camp.RED));
        initPoint(0, 3, new Shi(this, Camp.RED));
        initPoint(0, 5, new Shi(this, Camp.RED));
        initPoint(0, 2, new Xiang(this, Camp.RED));
        initPoint(0, 6, new Xiang(this, Camp.RED));
        initPoint(0, 1, new Ma(this, Camp.RED));
        initPoint(0, 7, new Ma(this, Camp.RED));
        initPoint(0, 0, new Che(this, Camp.RED));
        initPoint(0, 8, new Che(this, Camp.RED));
        initPoint(2, 1, new Pao(this, Camp.RED));
        initPoint(2, 7, new Pao(this, Camp.RED));
        initPoint(3, 0, new Bing(this, Camp.RED));
        initPoint(3, 2, new Bing(this, Camp.RED));
        initPoint(3, 4, new Bing(this, Camp.RED));
        initPoint(3, 6, new Bing(this, Camp.RED));
        initPoint(3, 8, new Bing(this, Camp.RED));
    }


    /**
     * 初始化一个位置
     *
     * @param row   行
     * @param col   列
     * @param chess 棋子
     */
    private void initPoint(int row, int col, Chess chess) {
        Position point = getPoint(row, col);
        point.setChess(chess);
        chess.setPosition(point);
        logger.info(String.format("(%s,%s)放置了一枚棋子:%s", row, col, chess));
    }

    /**
     * ARR[i][j]
     * 获得坐标点信息
     *
     * @return 棋盘上的一个点
     */
    @Override
    public Position getPoint(int row, int col) {
        return table.get(100 * row + col);
    }

    /**
     * @param camp 阵营
     * @return 根据阵营获得主将
     */
    public Chess getCommander(Camp camp) {
        for (Position point : getTable().values()) {
            Chess chess = point.getChess();
            if (chess != null && chess.getType() == ChessType.SHUAI) {
                if (chess.getCamp() == camp) {
                    return chess;
                }
            }
        }
        return null;
    }

    public boolean isOver() {
        return isOver;
    }


    /**
     * 双方将军的情况
     *
     * @return 将军的表
     */
    public Map<Camp, List<Chess>> checkMate() {
        Position blackCommander = getCommander(Camp.BLACK).getPosition();
        Position redCommander = getCommander(Camp.RED).getPosition();
        List<Chess> blackChess = new ArrayList<>();
        List<Chess> redChess = new ArrayList<>();
        for (Position point : getTable().values()) {
            Chess chess = point.getChess();
            if (chess != null) {
                if (chess.getType() == ChessType.SHUAI) { //跳过主将,因为两者之间无法互相将军,在将军类中特殊处理了
                    continue;
                }
                if (chess.getCamp() == Camp.BLACK) {
                    if (chess.getCanAttackPosition().contains(redCommander)) {
                        blackChess.add(chess);
                    }
                } else {
                    if (chess.getCanAttackPosition().contains(blackCommander)) {
                        redChess.add(chess);
                    }
                }

            }
        }
        Map<Camp, List<Chess>> map = new HashMap<>();
        map.put(Camp.BLACK, redChess);
        map.put(Camp.RED, blackChess);
        return map;
    }


    /**
     * @return 将军的消息
     */
    public CheckmateTip checkmateTipMsg() {
        Map<Camp, List<Chess>> campListMap = checkMate();
        List<Chess> redList = campListMap.get(Camp.BLACK);
        List<Chess> blackList = campListMap.get(Camp.RED);
        if (redList.isEmpty() && blackList.isEmpty()) {
            return null;
        }
        CheckmateTip.Builder checkmateTip = CheckmateTip.newBuilder();
        if (!redList.isEmpty()) {
            Chess commander = getCommander(Camp.BLACK);
            checkmateTip.setCommander((ChessNode) commander.getPosition().chessNodeMsg());
            for (Chess chess : redList) {
                checkmateTip.addCheck((ChessNode) chess.getPosition().chessNodeMsg());
            }
        } else {
            Chess commander = getCommander(Camp.RED);
            checkmateTip.setCommander((ChessNode) commander.getPosition().chessNodeMsg());
            for (Chess chess : blackList) {
                checkmateTip.addCheck((ChessNode) chess.getPosition().chessNodeMsg());
            }
        }
        return checkmateTip.build();
    }

    /**
     * @return 返回当前所有棋子信息
     */
    public CurrentBoard currentBoard() {
        CurrentBoard.Builder builder = CurrentBoard.newBuilder();
        for (Position position : table.values()) {
            if (position.getChess() != null)
                builder.addChessNode((ChessNode) position.chessNodeMsg());
        }
        return builder.build();
    }

    public static void main(String[] args) {
        Board chessBoard = new CHnChessBoard();
        chessBoard.init();
    }

}
